package com.euc.springbootframework.auth.service;

import com.euc.springbootframework.entity.Function;
import com.euc.springbootframework.entity.Role;
import com.euc.springbootframework.service.auth.RoleService;
import com.euc.springbootframework.service.auth.UserService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.HttpCookie;
import java.util.*;

import static com.euc.springbootframework.entity.QFunction.function;

@Component
public class TokenAuthenticationService {
    static final long EXPIRATIONTIME = 864_000_000; // 10 days
    static final String SECRET = "ThisIsASecret";
    static final String TOKEN_PREFIX = "Bearer";
    static final String HEADER_STRING = "Authorization";
    static final String HTTP_ONLY_STRING = "uid=112; Path=/; HttpOnly";

    private RoleService roleService;

    @Autowired
    public TokenAuthenticationService(RoleService roleService) {
        this.roleService = roleService;
    }

    public void addAuthentication(HttpServletResponse res, String username) {
        Map<String,Object> payload = new TreeMap<>();
        payload.put("username",username);
        List<Role> roles =  roleService.findRolesByUsername(username);
        payload.put("roles",roles);
        String JWT = Jwts.builder()
                .setClaims(payload)
                .setExpiration(new Date(System.currentTimeMillis()   + EXPIRATIONTIME))
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
        Cookie authCookie = new Cookie(HEADER_STRING,JWT);
        authCookie.setSecure(false);
        res.addCookie(authCookie);
        res.addHeader(HEADER_STRING,TOKEN_PREFIX + " " + JWT);
    }

    public Authentication getAuthentication(HttpServletRequest request,HttpServletResponse response) {
        String token = getAuthenticationToken(request);
        if(token == null){
            token = request.getHeader(HEADER_STRING);
        }else {
            token = TOKEN_PREFIX+ " " + token;
        }
        if (token != null && !token.replace(TOKEN_PREFIX, "").trim().equals("")) {
            // parse the token.
            String tmp = token.replace(TOKEN_PREFIX, "");
            Claims payload = Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(tmp)
                    .getBody();
            String username = payload.get("username",String .class);
            List roles = payload.get("roles",ArrayList.class);
            return username != null ?
                    new UsernamePasswordAuthenticationToken(username,null, getPayloadAuthorities(roles)) :
                    null;
        }
        return null;
    }

    public void removeAuthentication(HttpServletRequest request,HttpServletResponse response){
        Cookie authCookie = new Cookie(HEADER_STRING,"");
        authCookie.setMaxAge(-1);
        response.addCookie(authCookie);
        response.setHeader(HEADER_STRING, "");
    }

    public String getAuthenticationToken(HttpServletRequest request){
        Cookie[] cookies = request.getCookies();
        if(cookies!=null)
            for(Cookie cookie: cookies){
                if(cookie.getName().equals(HEADER_STRING)){
                    return cookie.getValue();
                }
            }
        return null;
    }

    public List<GrantedAuthority> getPayloadAuthorities(List<Object> roles){
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (Object role:roles) {
            authorities.add(new SimpleGrantedAuthority((String)((HashMap) role).get("name")));
            List<Object> functions = (List<Object>) ((HashMap) role).get("functions");
            for(Object function:functions) {
                String tmp = (String) ((HashMap) function).get("name");
                authorities.add(new SimpleGrantedAuthority(tmp));
            }
        }
        return authorities;
    }
}